﻿using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;

namespace CommonUtils
{
    public static class HttpUtil
    {
        public static int GetStatusCode(HttpStatusCode httpStatusCode)
        => (int)httpStatusCode;

        private static HttpClientHandler _handlerWithoutSslCheck;

        /// <summary>
        /// 初始化ClientHandler，须复用
        /// </summary>
        public static void InitHandler()
        {
            _handlerWithoutSslCheck = new HttpClientHandler();
            _handlerWithoutSslCheck.ServerCertificateCustomValidationCallback = delegate { return true; };
        }

        static HttpUtil()
        {
            InitHandler();
        }

        public static void ClearCookies()
        => InitHandler();

        public static string GetFromCookies(string url, string name)
        {
            var cookies = _handlerWithoutSslCheck.CookieContainer.GetCookies(new Uri(url));
            var cookie = cookies[name];
            if (cookie == null)
                return null;
            return cookie.Value;
        }

        public static HttpResponseMessage Send(HttpMethod method, string url, HttpContent content = null, Dictionary<string, string> headers = null, bool logError = true)
        {
            var message = new HttpResponseMessage();
            message.StatusCode = HttpStatusCode.ExpectationFailed;

            var client = new HttpClient(_handlerWithoutSslCheck);

            if (headers != null)
            {
                foreach (var header in headers)
                {
                    client.DefaultRequestHeaders.TryAddWithoutValidation(header.Key, header.Value);
                }
            }

            //ConsoleUtil.Write("RequestHeaders:");
            //client.DefaultRequestHeaders.PrintJson();

            try
            {
                switch (method)
                {
                    case HttpMethod.Get:
                        message = client.GetAsync(url).Result;
                        break;

                    case HttpMethod.Post:
                        message = client.PostAsync(url, content).Result;
                        break;

                    case HttpMethod.Delete:
                        message = client.DeleteAsync(url).Result;
                        break;

                    case HttpMethod.Put:
                        message = client.PutAsync(url, content).Result;
                        break;

                    case HttpMethod.Patch:
                        //message = client.PatchAsync(url, content).Result;
                        break;
                }
            }
            catch (Exception ex)
            {
                message.ReasonPhrase = ex.Message;
                if (logError)
                    LogUtil.Record(url, ex);
                else
                    LogUtil.PrintInfo(url + ex.Message);
            }

            //ConsoleUtil.Write("ResponseHeaders:");
            //message.Headers.PrintJson();

            LogUtil.PrintInfo(method + " " + url + " " + (int)message.StatusCode + " " + message.StatusCode);
            return message;
        }

        public static string AddParams(string url, Dictionary<string, string> args)
        {
            if (args == null || args.Count == 0)
                return url;
            url += url.Contains('?') ? '&' : '?';
            url += args.Select(m => m.Key + "=" + m.Value).Join('&');
            return url;
        }

        #region HttpMethod.Get
        public static HttpResponseMessage Get(string url, Dictionary<string, string> args = null, Dictionary<string, string> headers = null, bool log = true)
        => Send(HttpMethod.Get, AddParams(url, args), null, headers, log);

        public static HttpStatusCode GetStatusCode(string url, Dictionary<string, string> args = null, Dictionary<string, string> headers = null, bool log = true)
        => Get(url, args, headers, log).StatusCode;

        public static string GetString(string url, Dictionary<string, string> args = null, Dictionary<string, string> headers = null, bool log = true)
        => Get(url, args, headers, log).ReadString();

        public static Stream GetStream(string url, Dictionary<string, string> args = null, Dictionary<string, string> headers = null)
        => Get(url, args, headers).ReadStream();

        public static byte[] GetBytes(string url, Dictionary<string, string> args = null, Dictionary<string, string> headers = null)
        => Get(url, args, headers).ReadBytes();

        public static JToken GetJToken(string url, Dictionary<string, string> args = null, Dictionary<string, string> headers = null, bool log = true)
        => Get(url, args, headers, log).ReadJToken();

        public static JObject GetJObject(string url, Dictionary<string, string> args = null, Dictionary<string, string> headers = null, bool log = true)
        => Get(url, args, headers, log).ReadJObject();

        public static JArray GetJArray(string url, Dictionary<string, string> args = null, Dictionary<string, string> headers = null, bool log = true)
        => Get(url, args, headers, log).ReadJArray();

        public static bool Download(string url, string path, Dictionary<string, string> args = null, Dictionary<string, string> headers = null)
        {
            var message = Get(url, args, headers);
            if (message.StatusCode == HttpStatusCode.OK)
            {
                FileUtil.Save(path, message.ReadStream());
                ConsoleUtil.WriteLine("Saved to " + path);
                return true;
            }
            return false;
        }
        #endregion

        #region HttpMethod.Post
        public static HttpResponseMessage Post(string url, HttpContent content = null, Dictionary<string, string> headers = null, bool log = true)
        => Send(HttpMethod.Post, url, content, headers, log);

        public static HttpResponseMessage PostText(string url, string data, Encoding encoding = null, Dictionary<string, string> headers = null)
        => Post(url, new StringContent(data, encoding.NotNull(), ContentType.Text), headers);

        public static HttpResponseMessage PostJson(string url, string data, Encoding encoding = null, Dictionary<string, string> headers = null)
        => Post(url, new StringContent(data, encoding.NotNull(), ContentType.Json), headers);

        public static HttpResponseMessage PostForm(string url, string data, Encoding encoding = null, Dictionary<string, string> headers = null)
        => Post(url, new StringContent(data, encoding.NotNull(), ContentType.Form), headers);

        public static HttpResponseMessage PostFile(string url, string path, bool nameEncode = false, Dictionary<string, string> headers = null)
        {
            var streamContent = new StreamContent(File.OpenRead(path));
            var fileName = FileUtil.GetName(path);
            fileName = nameEncode ? fileName.UrlEncode() : fileName;

            var multipartFormDataContent = new MultipartFormDataContent();
            multipartFormDataContent.Add(streamContent, "file", fileName);

            return Post(url, multipartFormDataContent, headers);
        }

        public static HttpResponseMessage Upload(string url, string path, bool nameEncode = false, Dictionary<string, string> headers = null)
        => PostFile(url, path, nameEncode, headers);

        public static HttpResponseMessage PostFileByBase64(string url, string path)
        => PostForm(url, string.Format("name={0}&base64={1}", FileUtil.GetName(path), FileUtil.GetBase64(path)));

        public static HttpResponseMessage PostFileByJson(string url, string path)
        => PostJson(url, new { Name = FileUtil.GetName(path), Base64 = FileUtil.GetBase64(path) }.ToJson(true));
        #endregion

        public static HttpResponseMessage Delete(string url, Dictionary<string, string> headers = null, bool log = true)
        => Send(HttpMethod.Delete, url, null, headers, log);

        public static HttpResponseMessage Put(string url, HttpContent content = null, Dictionary<string, string> headers = null, bool log = true)
        => Send(HttpMethod.Put, url, content, headers, log);

        public static HttpResponseMessage PutJson(string url, string data, Encoding encoding = null, Dictionary<string, string> headers = null)
        => Put(url, new StringContent(data, encoding.NotNull(), ContentType.Json), headers);

        public static HttpResponseMessage Patch(string url, HttpContent content = null, Dictionary<string, string> headers = null, bool log = true)
        => Send(HttpMethod.Patch, url, content, headers, log);

        public static HttpResponseMessage PatchJson(string url, string data, Encoding encoding = null, Dictionary<string, string> headers = null)
        => Patch(url, new StringContent(data, encoding.NotNull(), ContentType.Json), headers);
    }
}
